package com.zjuee.gateway.filter;

import com.zjuee.base.bean.response.CommonResultCode;
import com.zjuee.base.bean.response.R;
import com.zjuee.base.exception.BaseException;
import com.zjuee.base.model.AuthUser;
import com.zjuee.core.utils.IpUtils;
import com.zjuee.core.utils.RedisUtil;
import com.zjuee.gateway.bean.MyToken;
import com.zjuee.gateway.utils.HttpResponseUtils;
import com.zjuee.gateway.utils.RSAUtil;
import com.zjuee.gateway.utils.SecurityUtil;
import com.zjuee.service.auth.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;


/**
 * 自定义登录认证Filter
 *
 * @author wu liang
 * @since 2020/8/13 9:54
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

  private final static Logger log = LoggerFactory.getLogger(MyFormAuthenticationFilter.class);

  private UserService userService;

  public void setUserService(UserService userService) {
    this.userService = userService;
  }

  // 校验登录的逻辑
  @Override
  protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
                                    Object mappedValue) {
    return super.isAccessAllowed(request, response, mappedValue);
  }

  // 未登录，会进入此方法；如果返回true表示需要继续处理；如果返回false表示该拦截器实例已经处理了，将直接返回即可。
  @Override
  protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
          throws Exception {

    //login  Url
    if (isLoginRequest(request, response)) {

      //post请求
      if (isLoginSubmission(request, response)) {
        if (log.isTraceEnabled()) {
          log.trace("Login submission detected.  Attempting to execute login.");
        }
        return executeLogin(request, response);
      }
      //非post请求
      else {
        if (log.isTraceEnabled()) {
          log.trace("Login page view.");
        }
        //直接向客户端相应错误消息
        HttpResponseUtils.sendJsonMessage(response, R.fail(CommonResultCode.UN_AUTH));
        return false;
      }
    }
    // 非登录url
    else {
      if (log.isTraceEnabled()) {
        log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                "Authentication url [" + getLoginUrl() + "]");
      }

      //向客户端响应错误消息
      HttpResponseUtils.sendJsonMessage(response, R.fail(CommonResultCode.UN_AUTH));
      return false;
    }
  }

  // 解密
  private String privateDecrypt(String str) {
    try {
      return RSAUtil.privateDecrypt(str, RSAUtil.getDefaultKey().getPrivateKeyStr());
    } catch (Exception e) {
      throw new BaseException(CommonResultCode.PW_ERR);
    }
  }

//  private static final Cache<String, String> ignoreCache = CacheBuilder.newBuilder()
//          .expireAfterWrite(10, TimeUnit.MINUTES)
//          .maximumSize(10000)
//          .build();

  /**
   * 创建Token
   */
  @Override
  protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
    String username = getUsername(request);
    String password = getPassword(request);
    String loginType = getLoginType(request);
    if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
//      throw new LoginException(CommonResultCode.LOGIN_FAIL);
    }

    // 尝试解密
    if (!"superadmin".equals(username)){
      if(StringUtils.equals("1",loginType)){
        username = privateDecrypt(username);
        password = privateDecrypt(password);
        username = username.replaceAll("\"","");
        password = password.replaceAll("\"","");
      }
      if (StringUtils.isBlank(username) || StringUtils.isBlank(password)
              || username.length() > 50 || password.length() > 50) {
        throw new BaseException(CommonResultCode.PW_ERR);
      }
    }

    MyToken myToken = new MyToken();
    myToken.setHost(getHost(request));
    myToken.setUsername(username);
    myToken.setPassword(password.toCharArray());
    // myToken.setRememberMe(isRememberMe(request));
    // 放弃自动登录
    myToken.setRememberMe(false);

    return myToken;
  }

  /**
   * 验证成功后走此方法 登录或失败都在此filter中直接返回，防止往下执行会重定向的问题
   */
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                   ServletRequest request, ServletResponse response) {
    HttpServletRequest httpRequest = WebUtils.toHttp(request);
    MyToken myToken = (MyToken) token;
    // 校验用户密码复杂度
//    if (ObjectUtil.notEqual("superadmin", myToken.getUsername()) && !String
//            .valueOf(myToken.getPassword()).matches(RegExpUtil.PASSWORD_REG)) {
//      subject.logout();
//      throw new LoginException(CommonResultCode.CONSUMER_ERR.getMsg());
//    }

    // 补充当前用户信息
    AuthUser user = (AuthUser) subject.getPrincipal();
    String ip = IpUtils.getIpAddr(httpRequest);
    user.setLoginIp(ip);
    user.setLoginTime(new Date());

    // region rememberMe
    user.setRememberHeaderKey(SecurityUtil.REMEMBER_KEY);
    Object base64Token = httpRequest.getAttribute(SecurityUtil.REMEMBER_REQUEST_KEY);
    user.setRememberValue(base64Token == null ? null : base64Token.toString());
    // endregion

//    UserAgent userAgent = UserAgent.parseUserAgentString(httpRequest.getHeader("User-Agent"));
//    if (userAgent != null) {
//      user.setUserBrowser(userAgent.getBrowser().getName());
//      user.setUserOs(userAgent.getOperatingSystem().getName());
//    }
    //更新用户表
//    User existUser = userService.updateLoginMessage(user.getUserId(), user.getLoginIp());
//    if (existUser != null) {
//      user.setLoginCount(existUser.getLoginCount());
//      user.setTypeStr(existUser.getTypeStr());
//      user.setOrg(existUser.getOrg());
//
//    }
    user.setAuthHeaderKey(SecurityUtil.AUTHORIZATION);
    user.setAuthValue(httpRequest.getSession().getId());
    // 调用RunAs委托的方式，更改Subject中用户的信息
    String realmName = subject.getPrincipals().getRealmNames().iterator().next();
    PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
    subject.runAs(newPrincipalCollection);

    // 清除错误登录次数
    cleanLock(ip + ":" + myToken.getUsername());
    HttpResponseUtils
            .sendJsonMessage(response, R.ok( user,"登录成功"));

    // 记录日志
    return false;
  }

  /**
   * 登陆失败后走此方法 返回false将不往后继续执行
   */
  @Override
  protected boolean onLoginFailure(AuthenticationToken authenticationToken,
                                   AuthenticationException e, ServletRequest request, ServletResponse response) {
    if (e != null) {
      // 自己抛出的登录异常
//      if (e instanceof LoginException) {
//        LoginException exception = (LoginException) e;
//        HttpResponseUtils.sendJsonMessage(response, R
//                .fail(exception.getMessage(), getMessage(authenticationToken, request, exception),
//                        exception.getObj()));
//        return false;
//      }

      // 认证过程中抛出的其他异常
      Throwable cause = e.getCause();
      if (cause != null) {
        if (cause instanceof BaseException) {
          BaseException exception = (BaseException) cause;
          HttpResponseUtils.sendJsonMessage(response,
                  R.fail(exception.getCode(), getMessage(authenticationToken, request, cause)));
          return false;
        }
      }
      HttpResponseUtils.sendJsonMessage(response,
              R.fail(CommonResultCode.CONSUMER_ERR, getMessage(authenticationToken, request, e)));
      return false;
    }
    log.error("登录失败", e);
    // 其他未知异常
    HttpResponseUtils.sendJsonMessage(response, R.fail(CommonResultCode.AUTH_EXP));
    return false;
  }

  private String getMessage(AuthenticationToken authenticationToken, ServletRequest request,
                            Throwable e) {
//    String msg = CommonResultCode.LOGIN_FAIL.getMsg();
    String msg = "";
//    if (e instanceof ResultCode) {
//      ResultCode resultCode = (ResultCode) e;
//      if (CommonResultCode.LOGIN_FAIL.getCode() != resultCode.getCode()) {
//        return resultCode.getMsg();
//      }
//    } else
      if (!(e instanceof AuthenticationException)) {
      log.error("登录异常", e);
      return msg;
    }

    // 标记 登录失败次数
    MyToken token = (MyToken) authenticationToken;
    HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
    String ip = IpUtils.getIpAddr(httpServletRequest);
    String key = ip + ":" + token.getUsername();
//    CaptUtils.lock(key);
//    if (CaptUtils.isLock(key)) {
//      return msg + "，错误次数超出限制，已被锁定" + CaptUtils.getLockDuration() + "分钟";
//    } else {
//      return msg + "，再输错" + CaptUtils.getValidCount(key) + "次，将被锁定" + CaptUtils.getLockDuration()
//              + "分钟";
//    }
    return null;
  }


  protected String getLoginType(ServletRequest request) {
    return WebUtils.getCleanParam(request, "loginType");
  }

  // 用户登录失败 标记前缀
  private static final String FAIL_COUNT_PREFIX = "LoginFailCount:";

  // 清除指定用户名的错误次数
  public static void cleanLock(String lockKey) {
    String key = RedisUtil.getPrefix() + FAIL_COUNT_PREFIX + lockKey;
//    cache.invalidate(key);
//    redisTemplate.delete(key);
  }
}
